"""
数据模型定义模块

定义了SchedulerAI系统中使用的核心数据结构，
包括工序、前后置关系、求解结果等。
"""

from dataclasses import dataclass
from typing import Any, Dict, Optional

from .exceptions import ValidationError


@dataclass
class Activity:
    """
    工序数据模型

    表示项目中的一个工序，包含ID、名称、工期等基本信息。
    """

    id: int
    name: str
    duration: int
    is_outdoor: bool = False

    def __post_init__(self) -> None:
        """数据验证"""
        if self.duration <= 0:
            raise ValidationError(
                "工期必须大于0",
                f"工序 {self.id} ({self.name}) 的工期为 {self.duration}",
            )

        if not isinstance(self.id, int) or self.id <= 0:
            raise ValidationError("工序ID必须为正整数", f"工序ID: {self.id}")

        if not self.name or not self.name.strip():
            raise ValidationError("工序名称不能为空", f"工序 {self.id} 的名称为空")


@dataclass
class Precedence:
    """
    前后置关系数据模型

    表示两个工序之间的依赖关系，包含依赖类型和延时。
    """

    from_id: int
    to_id: int
    type: str  # FS, SS, FF, SF
    lag: int = 0

    def __post_init__(self) -> None:
        """数据验证"""
        valid_types = ["FS", "SS", "FF", "SF"]
        if self.type not in valid_types:
            raise ValidationError(
                f"无效的依赖类型: {self.type}", f"有效类型: {', '.join(valid_types)}"
            )

        if self.from_id == self.to_id:
            raise ValidationError("工序不能依赖自己", f"工序 {self.from_id} 依赖自己")

        if not isinstance(self.from_id, int) or self.from_id <= 0:
            raise ValidationError(
                "前置工序ID必须为正整数", f"前置工序ID: {self.from_id}"
            )

        if not isinstance(self.to_id, int) or self.to_id <= 0:
            raise ValidationError("后续工序ID必须为正整数", f"后续工序ID: {self.to_id}")


@dataclass
class SolverResult:
    """
    求解结果数据模型

    包含CP-SAT求解器的求解状态、最优工期、详细调度等信息。
    """

    status: str  # OPTIMAL, FEASIBLE, INFEASIBLE, MODEL_INVALID, UNKNOWN
    makespan: Optional[int] = None
    schedule: Optional[Dict[int, Dict[str, Any]]] = None
    solve_time: Optional[float] = None
    error_message: Optional[str] = None

    @property
    def is_optimal(self) -> bool:
        """是否找到最优解"""
        return self.status == "OPTIMAL"

    @property
    def is_feasible(self) -> bool:
        """是否找到可行解"""
        return self.status in ["OPTIMAL", "FEASIBLE"]

    @property
    def is_infeasible(self) -> bool:
        """是否无解"""
        return self.status == "INFEASIBLE"

    @property
    def has_solution(self) -> bool:
        """是否有解"""
        return self.is_feasible and self.schedule is not None

    def __post_init__(self) -> None:
        """数据验证"""
        valid_statuses = [
            "OPTIMAL",
            "FEASIBLE",
            "INFEASIBLE",
            "MODEL_INVALID",
            "UNKNOWN",
        ]
        if self.status not in valid_statuses:
            raise ValidationError(
                f"无效的求解状态: {self.status}",
                f"有效状态: {', '.join(valid_statuses)}",
            )

        if self.is_feasible and self.makespan is not None and self.makespan < 0:
            raise ValidationError("总工期不能为负数", f"总工期: {self.makespan}")


@dataclass
class ScheduleResult:
    """
    调度结果数据模型

    包含完整的调度结果信息，用于业务层返回给用户。
    """

    success: bool
    solver_result: Optional[SolverResult] = None
    error: Optional[str] = None
    project_summary: Optional[Dict[str, Any]] = None

    @property
    def has_schedule(self) -> bool:
        """是否有有效的调度结果"""
        return (
            self.success
            and self.solver_result is not None
            and self.solver_result.has_solution
        )

    @property
    def makespan(self) -> Optional[int]:
        """获取总工期"""
        if self.has_schedule and self.solver_result is not None:
            return self.solver_result.makespan
        return None

    @property
    def schedule(self) -> Optional[Dict[int, Dict[str, Any]]]:
        """获取详细调度"""
        if self.has_schedule and self.solver_result is not None:
            return self.solver_result.schedule
        return None
